package ws.jdbc.concurrent.cursor;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Implementation of the {@link ICursorScrollReadOnly}.
 * 
 * <p>Since this cursor is scrollable the previous context must be restored.</p>
 *
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
public class CursorScrollReadOnly extends CursorForwardReadOnly implements ICursorScrollReadOnly {

  /**
   * Create new instance.
   *
   * @param rs {@link ResultSet} object that this cursor will work upon.
   */
  public CursorScrollReadOnly(ResultSet rs) {
    super(rs);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean next() throws SQLException {
    lock.lock();
    final boolean ret;
    try {
      restoreContext();
      currentCursor = id;
      ret = rs.next();
      if (ret) {
        currentRow++;
      } else {
        currentRow = AFTER_LAST;
      }
    } finally {
      lock.unlock();
    }
    return ret;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int getInt(int columnIndex) throws SQLException {
    lock.lock();
    final int ret;
    try {
      restoreContext();
      currentCursor = id;
      ret = rs.getInt(columnIndex);
    } finally {
      lock.unlock();
    }
    return ret;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String getString(int columnIndex) throws SQLException {
    lock.lock();
    final String ret;
    try {
      restoreContext();
      currentCursor = id;
      ret = rs.getString(columnIndex);
    } finally {
      lock.unlock();
    }
    return ret;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public float getFloat(int columnIndex) throws SQLException {
    lock.lock();
    final float ret;
    try {
      restoreContext();
      currentCursor = id;
      ret = rs.getFloat(columnIndex);
    } finally {
      lock.unlock();
    }
    return ret;
  }

  /**
   * {@inheritDoc}
   */
  public boolean absolute(int row) throws SQLException {
    lock.lock();
    final boolean ret;
    try {
      currentCursor = id;
      ret = rs.absolute(row);
      currentRow = row;
    } finally {
      lock.unlock();
    }
    return ret;
  }

  /**
   * {@inheritDoc}
   *
   * From Javadoc: "An attempt to position the cursor beyond the first/last
   * row in the result set leaves the cursor before the first row or after the last row."
   *
   * So if the currentRow is set to a value greater than the number of rows
   * in the result set, the next time the context is restored, the current
   * row of result set will be after the last row.
   */
  public void afterLast() throws SQLException {
    lock.lock();
    try {
      currentRow = AFTER_LAST;
      currentCursor = id;
      rs.afterLast();
    } finally {
      lock.unlock();
    }
  }

  /**
   * {@inheritDoc}
   *
   * From Javadoc: "An attempt to position the cursor beyond the first/last
   * row in the result set leaves the cursor before the first row or after the last row."
   *
   * So if the currentRow is set to a value lesser that one, the current row
   * of the result set will be set to before the first row, when the context
   * is restored.
   */
  public void beforeFirst() throws SQLException {
    lock.lock();
    try {
      currentRow = BEFORE_FIRST;
      currentCursor = id;
      rs.beforeFirst();
    } finally {
      lock.unlock();
    }
  }

  /**
   * {@inheritDoc}
   */
  public boolean previous() throws SQLException {
    lock.lock();
    final boolean ret;
    try {
      restoreContext();
      currentCursor = id;
      ret = rs.previous();
      if (ret) {
        currentRow--;
      } else {
        currentRow = BEFORE_FIRST;
      }
    } finally {
      lock.unlock();
    }
    return ret;
  }

  /**
   * {@inheritDoc}
   */
  public boolean relative(int rows) throws SQLException {
    lock.lock();
    final boolean ret;
    try {
      restoreContext();
      currentCursor = id;
      ret = rs.relative(rows);
      if (ret) {
        currentRow += rows;
      } else if (rows > 0) {
        currentRow = AFTER_LAST;
      } else {
        currentRow = BEFORE_FIRST;
      }

    } finally {
      lock.unlock();
    }
    return ret;
  }
}
